import {
    _decorator, Component, Node, Layout, UITransform, Size, instantiate, Prefab,
    resources,
    SpriteFrame,
    Sprite, v3, tween, sys,
    Label, LabelComponent, Vec3, CCInteger,
    Widget, director,
    Tween, NodeEventType, EventTouch, Camera, AudioClip
} from 'cc';
import {MathUtil} from "db://assets/scripts/MathUtil";
import {CandyItem} from "db://assets/scripts/xiaoxiao/CandyItem";
import {XiaoXiaoItemPosition} from "db://assets/scripts/xiaoxiao/XiaoXiaoItemPosition";
import {GameUser} from "db://assets/scripts/dto/GameUser";
import {get, post} from "db://assets/scripts/config/Api";
import {WxUser} from "db://assets/scripts/dto/WxUser";
import {Vec3DTO} from "db://assets/scripts/dto/Vec3DTO";
import {Vec2} from "../../../../../../ProgramData/cocos/editors/Creator/3.8.5/resources/resources/3d/engine/cocos/core";
import {Modal} from "db://assets/scripts/component/Modal";
import {AudioMgr} from "db://assets/scripts/component/AudioMgr";

const {ccclass, property} = _decorator;

class DirectionMer {
    direction: number;
    mergeNum: number;
}

@ccclass('XiaoXiaoGame')
export class XiaoXiaoGame extends Component {

    @property({type: CCInteger})
    row: number = 7;
    @property({type: CCInteger})
    column: number = 7;
    @property(Node)
    bodyParent: Node = null!;

    @property(Prefab)
    candyItemPrefab: Prefab = null!;

    @property([SpriteFrame])
    candyItemSprites: SpriteFrame[] = [];

    @property(LabelComponent)
    lvLabel: LabelComponent = null!;

    @property(LabelComponent)
    scroreLabel: LabelComponent = null!;

    @property(LabelComponent)
    maxScroreLabel: LabelComponent = null!;

    @property(LabelComponent)
    backLabel: LabelComponent = null!;

    bodyParentTrans: UITransform = null;
    userData: GameUser = null;
    squareArray: [XiaoXiaoItemPosition[]] = null;

    squarePreArray: XiaoXiaoItemPosition[] = [];

    touchCandyItemOne: Node = null;
    isGame = false;
    tweenTime = 0.6;
    userInfo: WxUser;

    currentScore: number = 0;
    @property(Node)
    canvas: Node = null!;


    touchTarget: Node;

    currentTouchStart: number = 0;

    touchStartPosition: Vec2 = null;

    mergeCount = 0;

    isClick = false;

    @property(AudioClip)
    bgMusic: AudioClip = null!;

    @property(AudioClip)
    sliderMusic: AudioClip = null;

    @property(AudioClip)
    mergeMusic: AudioClip = null;

    async start() {
        this.bodyParentTrans = this.bodyParent.getComponent(UITransform);
        this.userInfo = JSON.parse(localStorage.getItem('userInfo'));
       // AudioMgr.inst.play(this.bgMusic, true);
        await this.getUserInfo();
        this.initArray();
        this.updateView();
        this.init();
        this.mergeCount = 0;
        await this.merge(true);
    }

    update(deltaTime: number) {
        if (!this.isGame
            && this.mergeCount <= 0) {
            this.isGame = true;
        }
    }


    private initArray() {
        this.squareArray = [new Array<XiaoXiaoItemPosition>(this.userData.array.length)];
        for (let i = 0; i < this.userData.array.length; i++) {
            this.squareArray[i] = new Array(this.userData.array[i].length);
            for (let j = 0; j < this.userData.array[i].length; j++) {

                this.squareArray[i][j] = new XiaoXiaoItemPosition(v3(this.userData.array[i][j].x,
                    this.userData.array[i][j].y,
                    this.userData.array[i][j].z));
            }
        }
    }

    private async reset() {
        if (this.isGame) {
            this.currentScore = 0;
            this.isGame = false;
            this.touchCandyItemOne = null;

            if (this.bodyParent.children.length > 0) {
                this.bodyParent.destroyAllChildren();
                this.squareArray = null;
            }
            await this.gameOver();
            this.userData = await post<GameUser>("/game/startGameUser/xiaoxiaole");
            this.initArray();
            this.init();
            this.updateView();
            this.mergeCount = 0;
            await this.merge(true);
        }
    }

    private init() {
        let width = this.bodyParentTrans.contentSize.x;
        let height = this.bodyParentTrans.contentSize.y;
        let column: number = parseInt(this.userData.lv.split("*")[0]);

        let columnWidth = Math.floor((width - ((column - 1) * 10)) / (column));

        for (let i = 0; i < this.squareArray.length; i++) {
            for (let j = 0; j < this.squareArray[i].length; j++) {
                let arrayItem = this.squareArray[i][j];
                let itemObj = this.instantiCandy(arrayItem, columnWidth, width, height);
                this.squareArray[i][j].item = itemObj;
                this.squareArray[i][j].position = v3(itemObj.position.x, itemObj.position.y, itemObj.position.z);
                itemObj.scale = v3(0, 0, 0);
                tween(itemObj).to(this.tweenTime, {scale: v3(1, 1, 1)}, {easing: "sineInOut"}).start()
            }
        }

        // 预计生命体合成
        this.spawnPreSqure();
    }

    private instantiCandy(arrayItem: XiaoXiaoItemPosition, columnWidth: number, width: number, height: number) {
        let itemObj: Node = instantiate(this.candyItemPrefab);
        itemObj.parent = this.bodyParent;
        itemObj.name = "candy-" + arrayItem.row + "-" + arrayItem.column;
        let spriteFrame = this.candyItemSprites[arrayItem.type];
        // 左右,空出的距离
        itemObj.getComponent(CandyItem).init(spriteFrame, arrayItem, columnWidth, 20,
            width,
            height,
            this.bodyParent.getComponent(Widget).left,
            arrayItem.row,
            arrayItem.column
        );
        return itemObj;
    }

    public updateView() {
        this.lvLabel.string = this.column + "x" + this.row;
        this.scroreLabel.string = this.userData.score + "";
        this.maxScroreLabel.string = this.userInfo.gameXiaoxiaoMaxScore + "";
        this.backLabel.string = "撤回 (" + this.userData.backNum + ")";
    }

    private async getUserInfo() {
        // 从服务器加载
        this.userData = await get<GameUser>("/game/getLastGameUser/xiaoxiaole")
        // 游戏已经结束
        if (this.userData == null
            || this.userData.gameOver === 1) {
            this.userData = await post<GameUser>("/game/startGameUser/xiaoxiaole");
        }
    }


    private async merge(isInit: boolean): Promise<boolean> {
        return new Promise<boolean>(async (resolve, reject) => {

            let eliminateDirection: Map<XiaoXiaoItemPosition, DirectionMer> = new Map<XiaoXiaoItemPosition, DirectionMer>();

            let isEliminate = false;
            for (let i = 0; i < this.squareArray.length; i++) {
                let squareRow = this.squareArray[i];
                for (let j = 0; j < squareRow.length; j++) {
                    let square = squareRow[j];

                    // 1,2,3,4 上下左右

                    for (let k = 0; k < 4; k++) {
                        let mergeNum = this.findMerge(square, 1, k + 1);
                        if (mergeNum >= 3) {
                            // 消除
                            let directionmer: DirectionMer = new DirectionMer();
                            directionmer.direction = k + 1;
                            directionmer.mergeNum = mergeNum;
                            eliminateDirection.set(square, directionmer);
                        }
                    }


                }
            }


            // 消除
            eliminateDirection.forEach((value: DirectionMer, key: XiaoXiaoItemPosition) => {
                let score = this.eliminate(key, value.mergeNum, value.direction, isInit);
                if (!isInit) {
                    this.currentScore += score;
                    AudioMgr.inst.playOneShot(this.mergeMusic);
                }
                isEliminate = true;
            })

            if (isEliminate) {
                this.isGame = false;
                this.mergeCount++;
                console.log("开始合并1", new Date().getTime());
                // 滑动
                await this.sliding(isInit);
                console.log("开始合并2", new Date().getTime())
                await this.merge(isInit);
                console.log("开始合并3", new Date().getTime())
                this.mergeCount--;
            } else {
                if (isInit) {
                    this.setScore(this.currentScore);
                }
            }


            return resolve(isEliminate);
        });
    }


    /**
     * 消除方块
     * @private
     */
    private eliminate(square: XiaoXiaoItemPosition, num: number, direction: number, isInit: boolean): number {
        let scores = 0;
        while (num > 0) {
            if (!isInit)
                scores++;
            // console.log(`消除 ${square.row}-${square.column}`)

            // 第一次可能是空的,因为被不同行的消除过了
            if (square != null
                && square.item != null) {
                square.item.getComponent(CandyItem).playDestroy();
                this.squareArray[square.row][square.column].type = null;
                this.squareArray[square.row][square.column].item = null;
            }

            square = this.getNextSquare(square, direction);
            if (square == null) {
                break;
            }
            if (square.item == null) {
                break;
            }
            num--;
        }
        return scores;
    }

    private async gameOver() {
        this.userData.gameOver = 1;
        await post("/game/updateGameUser/" + this.userData.id, this.userData);
    }

    /**
     * 向下滑动方块
     * @private
     */
    private sliding(isInit: boolean): Promise<any> {
        return new Promise<boolean>((resolve, reject) => {


            let tweens: Array<Tween<Node>> = new Array();


            for (let i = this.squareArray.length - 1; i >= 0; i--) {
                for (let j = 0; j < this.squareArray[i].length; j++) {
                    let square = this.squareArray[i][j];
                    if (square.item == null) {
                        let topItem = this.findTop(square);

                        let topItemNode: Node = topItem.item;


                        topItemNode.scale = v3(1, 1, 1);


                        square.item = topItemNode;
                        square.type = topItem.type;
                        square.item.name = "candy-" + square.row + "-" + square.column;


                        topItem.item = null;
                        topItem.type = null;

                        if (topItem.row === -1) {
                            this.spawnPreSqure();
                        }
                        let squarePosition = v3(square.position.x, square.position.y, square.position.z);
                        topItemNode.getComponent(CandyItem).setInfo(square.row, square.column);
                        let tweenTime = this.tweenTime;
                        if (isInit) {
                            tweenTime = 0.2;
                        }
                        tweens.push(tween(square.item).to(tweenTime, {position: squarePosition}, {easing: "sineInOut"}));
                        //console.log(`滑动 item1:${topItem.row}-${topItem.column} 位置:${squarePosition}`)
                        //console.log(`滑动 item2:${square.row}-${square.column} 位置:${square.position}`)
                    }
                }
            }

            tween(this.node).parallel(...tweens)
                .call(() => {
                    resolve(true);
                })
                .start();
        })
    }

    /**
     * 查找上一个物体
     * @param square
     * @private
     */
    private findTop(square: XiaoXiaoItemPosition): XiaoXiaoItemPosition {
        for (let i = square.row - 1; i >= 0; i--) {
            if (this.squareArray[i][square.column].item != null) {
                return this.squareArray[i][square.column];
            }
        }
        // 没有找到，从预留最上层找到一个
        return this.squarePreArray[square.column];
    }

    /**
     * 判断左边是否有相同
     * @private
     * @param square
     * @param mergnCount
     * @param direction 1:左 2：右 3：上 4:下
     */
    private findMerge(square: XiaoXiaoItemPosition, mergnCount: number, direction: number): number {
        if (square.item == null) {
            return mergnCount;
        }
        let itemNext = this.getNextSquare(square, direction);
        if (itemNext == null) {
            return mergnCount;
        }
        if (itemNext.item == null) {
            return mergnCount;
        }
        if (itemNext.type === square.type) {
            return this.findMerge(itemNext, mergnCount + 1, direction)
        }
        return mergnCount;
    }

    public getNextSquareRowColumn(row: number, column: number, direction: number): XiaoXiaoItemPosition {

        return this.getNextSquare(this.squareArray[row][column], direction);
    }

    public getNextSquare(square: XiaoXiaoItemPosition, direction: number): XiaoXiaoItemPosition {
        let itemNext = null;
        // 左
        if (direction == 1) {
            if (square.column - 1 < 0) {
                return null;
            }
            itemNext = this.squareArray[square.row][square.column - 1];
        }
        // 右
        else if (direction == 2) {
            if (square.column + 1 >= this.column) {
                return null;
            }
            itemNext = this.squareArray[square.row][square.column + 1];
        }
        // 上
        else if (direction == 3) {
            if (square.row - 1 < 0) {
                return null;
            }
            itemNext = this.squareArray[square.row - 1][square.column];
        }
        // 下
        else if (direction == 4) {
            if (square.row + 1 >= this.row) {
                return null;
            }
            itemNext = this.squareArray[square.row + 1][square.column];
        }
        return itemNext;
    }


    private printArray() {
        for (const squareArrayElement of this.squareArray) {
            for (const itemPosition of squareArrayElement) {
                console.log(`row:${itemPosition.row},column:${itemPosition.column},type:${itemPosition.type},position:${itemPosition.position}  nodeposition:${itemPosition.item.position}`)
            }
        }
    }


    private copyArray(): [XiaoXiaoItemPosition[]] {
        let newArray: [XiaoXiaoItemPosition[]] = [[]];

        for (let i = 0; i < this.squareArray.length; i++) {
            newArray[i] = new Array(this.squareArray[i].length);
            for (let j = 0; j < this.squareArray[i].length; j++) {
                let squareArrayElementElement = this.squareArray[i][j];
                newArray[i][j] = new XiaoXiaoItemPosition(v3(squareArrayElementElement.row, squareArrayElementElement.column, squareArrayElementElement.type));
            }
        }
        return newArray;
    }

    private spawnPreSqure() {
        let width = this.bodyParentTrans.contentSize.x;
        let height = this.bodyParentTrans.contentSize.y;

        let columnWidth = Math.floor((width - ((this.column - 1) * 10)) / (this.column));
        this.squarePreArray = [];
        for (let i = 0; i < this.column; i++) {

            if (this.squarePreArray[i] == null
                || this.squarePreArray[i]?.item == null) {
                //  console.log('初始化spawnPreSqure')
                let number = MathUtil.random(0, this.candyItemSprites.length);
                this.squarePreArray[i] = new XiaoXiaoItemPosition(v3(-1, i, number));
                let itemObj = this.instantiCandy(this.squarePreArray[i], columnWidth, width, height);
                this.squarePreArray[i].item = itemObj;
                this.squarePreArray[i].position = v3(itemObj.position.x, itemObj.position.y, itemObj.position.z);
                itemObj.scale = v3(0, 0, 0);
            }
        }
    }

    public async touchCandyItemClick(candyItem: CandyItem) {
        //
        AudioMgr.inst.playOneShot(this.sliderMusic);

        this.isClick = true;
        if (this.touchCandyItemOne == null) {
            this.touchCandyItemOne = candyItem.node;
            candyItem.setSelect();
            this.isClick = false;
            return;
        }

        // 点的是第一个，取消
        if (this.touchCandyItemOne === candyItem.node) {
            this.touchCandyItemOne = null;
            candyItem.hideSelect();
            this.isClick = false;
            return;
        }

        // 校验是否在附近
        let candyItemOne: CandyItem = this.touchCandyItemOne.getComponent(CandyItem);
        let isNear = false;
        if (candyItemOne.row + 1 === candyItem.row
            && candyItemOne.column === candyItem.column) {
            isNear = true;
        } else if (candyItemOne.row - 1 === candyItem.row
            && candyItemOne.column === candyItem.column) {
            isNear = true;
        } else if (candyItemOne.row === candyItem.row
            && candyItemOne.column + 1 === candyItem.column) {
            isNear = true;
        } else if (candyItemOne.row === candyItem.row
            && candyItemOne.column - 1 === candyItem.column) {
            isNear = true;
        }

        if (!isNear) {
            this.touchCandyItemOne = null;
            candyItemOne.hideSelect();
            this.isClick = false;
            return;
        }
        this.isGame = false;
        this.currentScore = 0;
        this.touchCandyItemOne = null;
        // 交换位置
        this.exchangePosition(candyItemOne, candyItem, async () => {

            candyItemOne.hideSelect();

            //   console.log('交换完成位置1', candyItemOne.node.position)
            //  console.log('交换完成位置2', candyItem.node.position)
            this.mergeCount = 0;
            let isMerge = await this.merge(false);
            // 没有合并,交换回去
            if (!isMerge) {
                //    console.log("没有合并,交换回去");
                this.exchangePosition(candyItem, candyItemOne, () => {
                    this.isGame = true;
                    this.isClick = false;
                });
            } else {
                this.isClick = false;
                this.setScore(this.currentScore);
            }
        });
    }


    /**
     * 撤回
     * @private
     */
    private async cancel() {
        // 清除
        if (this.userData.backNum > 0
            && this.userData.arrayHistory.length > 2
            && this.isGame) {

            if (this.bodyParent.children.length > 0) {
                this.bodyParent.destroyAllChildren();
                this.squareArray = null;
            }

            this.userData.backNum--;
            this.userData.arrayHistory.pop();
            this.userData.array = this.userData.arrayHistory.pop();
            this.userData.arrayHistory.push(this.userData.array);
            this.initArray();
            this.init();
            this.userData = await post<GameUser>("/game/updateGameUser/" + this.userData.id, this.userData);
            this.updateView();
        }

    }

    private exchangePosition(candyItem1: CandyItem, candyItem2: CandyItem, call ?: () => void) {
        this.isGame = false;

        // 交换位置
        let square1 = this.squareArray[candyItem1.row][candyItem1.column];
        let square2 = this.squareArray[candyItem2.row][candyItem2.column];
        //console.log('square1Position', square1.position)
        // console.log('square2Position', square2.position)
        let tempNode = square1.item;
        let tempType = square1.type;
        let tempRow = square1.row;
        let tempColumn = square1.column;


        square1.item = square2.item;
        square1.type = square2.type;


        square2.item = tempNode;
        square2.type = tempType;

        square1.item.name = "candy-" + square1.row + "-" + square1.column;
        square2.item.name = "candy-" + square2.row + "-" + square2.column;


        // 移动
        candyItem1.setInfo(candyItem2.row, candyItem2.column);
        candyItem2.setInfo(tempRow, tempColumn);

        let candyItem1Positiom = square1.position;
        let candyItem2Positiom = square2.position;
        let tween1 = tween(candyItem1.node)
            .to(0.3, {position: candyItem2Positiom}, {easing: "sineInOut"});

        let tween2 = tween(candyItem2.node).to(0.3, {position: candyItem1Positiom}, {easing: "sineInOut"});

        tween(this.node).parallel(tween1, tween2)
            .call(() => {
                if (call != null) {
                    call();
                }
            })
            .start();

    }

    private async setScore(score: number) {
        console.log("设置分数")
        this.userData.array = this.transArray();
        this.userData.arrayHistory.push(this.userData.array);
        this.userData.score = this.userData.score + score;


        try {
            post<GameUser>("/game/updateGameUser/" + this.userData.id, this.userData).then(res => {
                this.userData = res;
                if (this.userData.maxScore != null
                    && this.userData.maxScore != 0) {
                    console.log("更新最高分数", this.userData.maxScore)
                    this.userInfo.gameXiaoxiaoMaxScore = this.userData.maxScore;
                    sys.localStorage.setItem('userInfo', JSON.stringify(this.userInfo));
                }
                this.updateView();
            })
        } catch (e) {
            console.log("异常", e)
        }
    }


    private transArray(): [Vec3DTO[]] {
        let vect3Array: [Vec3DTO[]] = [new Array<Vec3DTO>(this.squareArray.length)];
        for (let i = 0; i < this.squareArray.length; i++) {
            vect3Array[i] = new Array(this.squareArray[i].length);
            for (let j = 0; j < this.squareArray[i].length; j++) {

                vect3Array[i][j] = new Vec3DTO();
                vect3Array[i][j].x = this.squareArray[i][j].row
                vect3Array[i][j].y = this.squareArray[i][j].column
                vect3Array[i][j].z = this.squareArray[i][j].type
            }
        }
        return vect3Array;
    }

    public clickHome() {
        director.loadScene("home");
    }

    /**
     * 打开游戏说明
     * @private
     */
    private openGameDesc() {
        Modal.Instance.show("消消乐游戏说明", `1.滑动手指，让三个及以上相同元素横向或纵向相连，即可触发消除。
2.玩家横纵或纵向有三个相连,视为有效滑动。
3.有效滑动后会增加得分,并会生成新的图案。
4.棋盘被图案填满，并且进行有效合并，判负，游戏结束!
        `);
    }
}
